
#import "XmlRpcEncoder.h"

@implementation XmlRpcEncoder

//
// method name
//
@synthesize methodName														= _methodName;

//
// parameters
//
@synthesize parameterList													= _parameterList;

//
// init
//
- (id)init
{
	//
	// call super
	//
	self																	= [super init];
	if(!self)
		return nil;
	
	self.parameterList														= [NSMutableArray arrayWithCapacity:1];
	
	return self;
}

//
// dealloc
//
- (void)dealloc
{
	self.methodName															= nil;
	self.parameterList														= nil;
	
	[super dealloc];
}

//
// encode value tag
//
- (NSString*)encodeValue:(NSString*)theValue forTag:(NSString*)theTag
{
	return [NSString stringWithFormat:@"<value><%@>%@</%@></value>", theTag, theValue, theTag];
}

//
// encode object
//
- (NSString*)encodeObject:(id)theObject
{
	if(!theObject)
		return nil;
	
	if([theObject isKindOfClass:[NSArray class]])
	{
		NSMutableString* retValue											= [NSMutableString string];
		[retValue appendString:@"<value><array><data>"];
		for(id child in theObject)
			[retValue appendString:[self encodeObject:child]];
		[retValue appendString:@"</data></array></value>"];
		
		return retValue;
	}
	
	if([theObject isKindOfClass:[NSDictionary class]])
	{
		NSMutableString* retValue											= [NSMutableString string];
		[retValue appendString:@"<value><struct>"];
		for(NSString* theKey in (NSDictionary*)theObject)
		{
			[retValue appendString:@"<member><name>"];
			[retValue appendString:[theKey encodeEntity]];
			[retValue appendString:@"</name>"];
			[retValue appendString:[self encodeObject:[theObject objectForKey:theKey]]];
			[retValue appendString:@"</member>"];
		}
		[retValue appendString:@"</struct></value>"];
		return retValue;
	}
	
	if((CFBooleanRef)theObject == kCFBooleanTrue || (CFBooleanRef)theObject == kCFBooleanFalse)
		return [self encodeValue:[theObject boolValue] ? @"1" : @"0" forTag:@"boolean"];
	
	if([theObject isKindOfClass:[NSNumber class]])
	{
		if(!strcmp([theObject objCType], "d"))
			return [self encodeValue:[theObject stringValue] forTag:@"double"];
		
		return [self encodeValue:[theObject stringValue] forTag:@"i8"];
	}
	
	if([theObject isKindOfClass:[NSDate class]])
	{
		NSUInteger components												= kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay | kCFCalendarUnitHour | kCFCalendarUnitMinute | kCFCalendarUnitSecond;
		NSDateComponents* date												= [[NSCalendar currentCalendar] components:components fromDate:theObject];
		return [self encodeValue:[NSString stringWithFormat:@"%.4d%.2d%.2dT%.2d:%.2d:%.2d", date.year, date.month, date.day, date.hour, date.minute, date.second] forTag:@"dateTime.iso8601"];
	}
	
	if([theObject isKindOfClass:[NSData class]])
		return [self encodeValue:[theObject encodeBase64] forTag:@"base64"];
	
	return [self encodeValue:[theObject encodeEntity] forTag:@"string"];
}

//
// encode
//
- (NSString*)encode
{
	NSMutableString* retValue												= [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>"];
	[retValue appendString:[self.methodName encodeEntity]];
	[retValue appendString:@"</methodName><params>"];
	for(id parameter in self.parameterList)
	{
		[retValue appendString:@"<param>"];
		[retValue appendString:[self encodeObject:parameter]];
		[retValue appendString:@"</param>"];
	}
	[retValue appendString:@"</params></methodCall>"];
	return retValue;
}

@end
